151. 反转字符串中的单词

151. 反转字符串中的单词

题目链接
代码随想录

分析

其实这个题的思路很简单,先移除多余空格,然后反转整个字符串,再反转单词对应的字符串
反转整个字符串这一步我们就不说了,跟 344. 反转字符串 一模一样,移除多余空格和反转字符串中的单词写起来还是有一点难度的

移除空格

移除空格部分的代码跟 27. 移除元素 的方法其实是一样的,就是双指针法。
常规的方法是先移除头部的空格,再移除尾部的空格,最后移除中间部分的空格,去除中间空格的时候,首先判断当前 fast 指针指向的字符,是不是空格,如果不是,则说明是目标字符,同时移动快慢指针,如果是,则查看上一个确定的字符(slow 指向的字符)是不是空格,如果不是,则也是目标字符,同时移动快慢指针,如果是,说明当前是连续空格字符,不是目标字符

// 移除空格
public String removeSpace(char[] chars){
    // 还是用双指针法
    int left = 0, right = chars.length-1;
    // 移除头部的空格
    while(chars[left]==' '){
        left++;
    }
    // 移除尾部的空格
    while(chars[right]==' '){
        right--;
    }
    // 此时 left 和 right 指向的都是不为空格的字符
    // slow 指的是下一个要插入的位置
    int slow =left+1;
    // fast 指向的是下一个需要检查的位置
    int fast =left+1;
    while(fast<=right){
	    // 如何移除中间的空格呢?
	    // 如果当前遍历字符不是空格,则同时移动快慢指针,
	    // 如果当前遍历字符是空格,而且上一个确定的字符不是空格,则同时移动快慢指针,
	    // 如果当前遍历字符是空格,而且上一个确定的字符也是空格,则仅移动快指针,即跳过此字符。
        if(chars[fast]!=' '||chars[slow-1]!=' '){
            chars[slow]=chars[fast];
            slow++;
            fast++;
        }else{
            fast++;
        }
    }
    // 此时slow指的是下一个位置的索引,copyOfRange的第三个参数指向的索引刚好是排除的。
    return new String(Arrays.copyOfRange(chars, left, slow));
}

其实也可以不用分三步,可以一步到位,但是比较难想

public String removeSpace(char[] chars){
    int slow = 0, fast = 0;
    while (fast < chars.length) {
        // 如果当前字符是空格,而且
        // 这是第一个字符或是最后一个字符,那肯定都是跳过
        // 这个  ' ' == chars[fast + 1] 是为了处理末尾连续多个空格
        if(' ' == chars[fast] && (fast == 0  ||  fast==chars.length-1 || ' ' == chars[fast + 1] || slow == 0 || ' ' == chars[slow - 1])){
            fast++;
        }else{
            chars[slow] = chars[fast];
            fast++;
            slow++;
        }
    }
    // 此时slow指的是下一个位置的索引,copyOfRange的第三个参数指向的索引刚好是排除的。
    return new String(Arrays.copyOfRange(chars, 0, slow));
}

反转单词

反转单词最终的是找到单词的起点和重点,
如果上一个字符没有或者上一个字符是空格,那么当前就是单词的起点
如果当前字符不为空格,但是是字符串的末尾,或者不是末尾且下一个字符是空格,那么当前就是单词的末尾

public String reverseWordInStr(char[] chars) {
    char lastChar = 0;
    int wordStart = 0, wordEnd = 0;
    for (int i = 0; i < chars.length; i++) {
        // 如果lastChar不存在或者lastChar是空格,当前就是单词的起点
        if ((lastChar == 0 || ' ' == lastChar)) {
            wordStart = i;
        }
        // 如果当前不是空格,下一个是空格,或者当前就是字符串的末尾了
        if (' ' != chars[i] && (i == chars.length - 1 || ' ' == chars[i + 1])) {
            wordEnd = i;
            // 开始反转
            for (; wordStart < wordEnd;) {
                char temp = chars[wordStart];
                chars[wordStart] = chars[wordEnd];
                chars[wordEnd] = temp;
                wordStart++;
                wordEnd--;
            }
            wordStart = 0;
            wordEnd = 0;
        }
        lastChar = chars[i];
    }
    return new String(chars);
}

解题

class Solution {
    public String reverseWords(String s) {
        // 反转两次即可
        // 先把整个字符串反转,然后再把单词反转
        char[] chars = s.toCharArray();
        String cleanStr = removeSpace(chars);
        chars = cleanStr.toCharArray();
        int left = 0, right = chars.length - 1;
        for (; left < right;) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
        // 开始反转单词
        return reverseWordInStr(chars);
    }

    public String removeSpace(char[] chars) {
        // 还是用双指针法
        int left = 0, right = chars.length - 1;
        // 移除头部的空格
        while (chars[left] == ' ') {
            left++;
        }
        // 移除尾部的空格
        while (chars[right] == ' ') {
            right--;
        }
        // 此时 left 和 right 指向的都是不为空格的字符
        // slow 指的是下一个要插入的位置
        int slow = left + 1;
        // fast 指向的是下一个需要检查的位置
        int fast = left + 1;
        while (fast <= right) {
            // 如何移除中间的空格呢?
            // 如果当前遍历字符不是空格,则同时移动快慢指针,
            // 如果当前遍历字符是空格,而且上一个确定的字符不是空格,则同时移动快慢指针,
            // 如果当前遍历字符是空格,而且上一个确定的字符也是空格,则仅移动快指针,即跳过此字符。
            if (chars[fast] != ' ' || chars[slow - 1] != ' ') {
                chars[slow] = chars[fast];
                slow++;
                fast++;
            } else {
                fast++;
            }
        }
        // 此时slow指的是下一个位置的索引,copyOfRange的第三个参数指向的索引刚好是排除的。
        return new String(Arrays.copyOfRange(chars, left, slow));
    }

    public String reverseWordInStr(char[] chars) {
        char lastChar = 0;
        int wordStart = 0, wordEnd = 0;
        for (int i = 0; i < chars.length; i++) {
            // 如果lastChar不存在或者lastChar是空格,当前就是单词的起点
            if ((lastChar == 0 || ' ' == lastChar)) {
                wordStart = i;
            }
            // 如果当前不是空格,下一个是空格,或者当前就是字符串的末尾了
            if (' ' != chars[i] && (i == chars.length - 1 || ' ' == chars[i + 1])) {
                wordEnd = i;
                // 开始反转
                for (; wordStart < wordEnd;) {
                    char temp = chars[wordStart];
                    chars[wordStart] = chars[wordEnd];
                    chars[wordEnd] = temp;
                    wordStart++;
                    wordEnd--;
                }
                wordStart = 0;
                wordEnd = 0;
            }
            lastChar = chars[i];
        }
        return new String(chars);
    }

}

相关题

344. 反转字符串